GAE で Stackdriver Logging にログを拾わせてトレースしたい
やりたいこと
エラーログやアプリケーションログを出したい
構造化したログを吐きたい
textPayload ではなく jsonPayload にしたい
GAE のアクセスログとアプリケーションから出力するログを対応付けたい
Stackdriver Logging のクライアントをアプリケーションから使いたくない、標準出力/標準エラー出力で済ませたい
やり方
これの JSON ログフィールド をキーとした JSON を標準出力/標準エラー出力に書き出す
アプリケーションから入れるのは severity や message
なくても勝手に紐づく?
trace と spanId は X-Cloud-Trace-Context リクエストヘッダに入ってやってくるのでバラしてログに入れる
X-Cloud-Trace-Context: fdddafd26d20124266fc7d4465c70806/12935805563810517035 なら
logging.googleapis.com/trace: "projects/{ProjectID}/traces/fdddafd26d20124266fc7d4465c70806"
logging.googleapis.com/spanId: "12935805563810517035"
を出力するとヒモづく
ログ例
code:log.json
{
"severity": "info",
"message": "application log",
"timestampSeconds": ...,
"timestampNanos": ...,
"logging.googleapis.com/trace": "projects/{ProjectID}/traces/fdddafd26d20124266fc7d4465c70806",
"logging.googleapis.com/spanId": "12935805563810517035",
...
}
Stackdriver Logging の表示
https://gyazo.com/60c4562910c6d245a104b404a46d5aaa
アプリケーションから標準出力に書いたログが先に出ている
* のアクセスログにアプリケーションログが紐付いている
アプリケーションログの行に出てくる文字列は message フィールド
紐付いたところから、アプリケーションログへのリンクはゲットできるが直接は飛べない
trace をクリックするとフィルタへのリンクが出てくる
その場で payload を展開できない
trace の値で検索するのが楽
ミドルウェア作って Context で Trace 情報を引き回す
code:middelware.go
type middlewareContextKey string
const traceKey middlewareContextKey = "Trace"
type Trace struct {
TraceID string
SpanID string
}
// XXX 必要になってからパースする & Context にキャッシュする、という実装の方がパフォーマンス出そう
func StackdriverTrace() gin.HandlerFunc {
return func(c *gin.Context) {
trace := Trace{}
value := c.Request.Header.Get("X-Cloud-Trace-Context")
if value != "" {
params := strings.Split(value, "/")
if len(params) == 2 {
}
}
ctx := context.WithValue(c.Request.Context(), traceKey, trace)
c.Request = c.Request.WithContext(ctx)
c.Next()
}
}
func GetStackdriverTrace(req *http.Request) Trace {
trace, _ := req.Context().Value(traceKey).(Trace)
return trace
}
実際はエラーログとロガーインスタンスを分けたり、他のフィールドを入れれるように func 作ったり
code:logger.go
import (
...
log "github.com/sirupsen/logrus"
)
func init() {
log.SetFormatter(&log.JSONFormatter{
FieldMap: log.FieldMap{
log.FieldKeyLevel: "severity",
log.FieldKeyMsg: "message",
},
DisableTimestamp: true,
})
log.SetOutput(os.Stdout)
}
...
trace := GetStackdriverTrace(c.Request)
log.WithFields(log.Fields{
"timestampSeconds": now.Unix(),
"timestampNanos": now.Nanosecond(),
"logging.googleapis.com/trace": fmt.Sprintf("projects/%s/traces/%s", ProjectID, trace.TraceID)
"logging.googleapis.com/spanId": trace.SpanID
}).Info("application log")
参考